Skip to content

feat(bin): add standalone morph-statetest runner#113

Merged
chengwenxi merged 5 commits into
mainfrom
feat/morph-statetest-binary
May 26, 2026
Merged

feat(bin): add standalone morph-statetest runner#113
chengwenxi merged 5 commits into
mainfrom
feat/morph-statetest-binary

Conversation

@panos-xyz
Copy link
Copy Markdown
Contributor

Summary

  • Adds standalone morph-statetest <path> binary that runs a GeneralStateTest with Morph EVM semantics
  • Outputs EIP-3155 trace/result according to the contract documented in morph-cross-client-tests/docs/statetest-contract.md
  • Enables cross-client differential testing via morph-l2/morph-cross-client-tests
  • Supports Morph fork names (Bernoulli, Curie, Morph203, Viridian, Emerald, Jade) in post.<fork> fixture keys
  • MorphTx (type 0x7f) and L1Msg (type 0x7e) transaction fields: feeTokenID, feeLimit, version, reference, memo
  • JSON Lines output to stdout; EIP-3155 opcode trace to stderr when --trace is passed

Test plan

  • cargo test -p morph-statetest
  • cargo clippy -p morph-statetest --all-targets -- -D warnings
  • morph-statetest testdata/fee_token_call_jade.json produces valid JSON Lines output
  • Cross-validated against morph-geth evm --json statetest: 0/3900 mismatches on full 39-engine sweep across Jade fork

panos-xyz added 2 commits May 26, 2026 10:23
morph-statetest's schema was routing the JSON `type` field through
revm's `TransactionType::from(u8)`, which only knows the 5 vanilla
Ethereum tx types (Legacy=0, Eip2930=1, Eip1559=2, Eip4844=3,
Eip7702=4). Any other value -- including Morph's `0x7F` MorphTx and
`0x7E` L1Msg -- falls into `Custom`, which has discriminant `0xFF`.
The subsequent `as u8` cast then yields `0xFF`, so `TxEnv.tx_type =
0xFF` instead of the intended raw byte.

Downstream, `MorphTxExt::is_morph_tx` checks `tx_type ==
MORPH_TX_TYPE_ID (0x7F)` and `is_l1_msg` checks against
`L1_TX_TYPE_ID (0x7E)`. Both returned false for every Morph-typed
statetest fixture, so:

  - The MorphTx fee handler dispatch in `crates/revm/src/handler.rs`
    (`if evm.ctx_ref().tx().is_morph_tx() { ... }`) silently fell
    through to the standard ETH-fee branch, even when `feeTokenID > 0`.
  - The L1Msg short-circuit in the same handler never activated.

Empirically caught 2026-05-18 via the morph-cross-client-tests harness
during investigation of fuzz coverage in fee_token_call. After the
matching fix landed on the geth side (gen_sttransaction.go now parses
feeTokenID instead of dropping it), geth produced a stateRoot
distinct from the no-fee-fields baseline while reth's stateRoot
stayed at the baseline -- confirming reth's alt-token fee path was
never activating in statetest. With the raw-byte fix here, reth's
fee handler engages and now produces a stateRoot that differs from
the baseline too.

Fix: short-circuit the enum round-trip when JSON explicitly supplies a
`type` field. Only fall through to revm's `TransactionType` inference
for fixtures without a `type` (where the enum's standard-types-only
view is sufficient anyway).
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, reopen this pull request to trigger a review.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Warning

Review limit reached

@panos-xyz, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 37 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bcec8d11-a2b6-412e-9151-e8cc0555bbf9

📥 Commits

Reviewing files that changed from the base of the PR and between 18e7961 and fbff3f4.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • Cargo.toml
  • bin/morph-statetest/Cargo.toml
  • bin/morph-statetest/src/lib.rs
  • bin/morph-statetest/src/main.rs
  • bin/morph-statetest/src/runner.rs
  • bin/morph-statetest/src/schema.rs
  • crates/revm/src/tx.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/morph-statetest-binary

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

panos-xyz added 3 commits May 26, 2026 14:56
…back with go-ethereum

- Set block.beneficiary to the Morph fee vault on Curie+ so statetest
  fee distribution matches morph-geth's FeeVaultEnabled path.
- Build the per-tx receipt log set from pre-fee + EVM + post-fee logs so
  logsRoot covers the full receipt surface (not just EVM execution logs).
- Plumb post.txbytes + has_base_fee into morph_tx_env; when txbytes is
  absent, fall back to encode_for_l1_fee so L1 data fee accounting
  matches the geth EstimateL1DataFeeForMessage path.
- Default gas_priority_fee to gas_price when has_base_fee is true,
  matching the geth-side gasTipCap default for legacy/EIP-2930 txs.
- Switch encode_for_l1_fee's placeholder signature to (U256::MAX,
  U256::MAX, true) so the encoded byte length matches go-ethereum's
  0xff...ff placeholder and L1 fee sizing is identical.
…tly zeroing L1 fee

morph-reth's transaction envelope has no Eip4844 variant
(crates/primitives/src/transaction/envelope.rs), so encode_for_l1_fee
cannot fabricate one for a blob tx. The schema previously skipped
fallback RLP generation for blob_hashes != empty and let it fall
through to handler.rs's rlp_bytes.unwrap_or_default(), which produces
empty bytes and therefore L1 fee = 0. A future blob-tx fuzz reactivation
would silently diverge from go-ethereum's L1 fee accounting with no
hint of why.

Surface the impossibility at parse time with a new SchemaError
variant. Add a test that asserts blob_hashes != empty without
post.txbytes errors out with BlobTxRequiresTxBytes.
The cherry-picked source commits predate the rustfmt config currently
enforced by CI. Re-runs only `cargo fmt --all`, no semantic changes.
@panos-xyz panos-xyz force-pushed the feat/morph-statetest-binary branch from 011bf07 to fbff3f4 Compare May 26, 2026 06:57
@chengwenxi chengwenxi merged commit 1e954a2 into main May 26, 2026
13 checks passed
@chengwenxi chengwenxi deleted the feat/morph-statetest-binary branch May 26, 2026 07:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants